﻿using System;
using System.Collections.Generic;

using Microsoft.StyleCop;
using Microsoft.StyleCop.CSharp;

namespace StyleCopContrib.CustomRules.Analyzers
{
	/// <summary>
	/// A custom rule analyzer class.
	/// </summary>
	[SourceAnalyzer(typeof(CsParser))]
	public sealed class CustomAnalyzer : SourceAnalyzer
	{
		#region Fields

		private AnalyzerRegistry _analyzerRegistry;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="CustomAnalyzer"/> class.
		/// </summary>
		public CustomAnalyzer()
		{
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets the settings pages.
		/// </summary>
		/// <value>The settings pages.</value>
		public override ICollection<IPropertyControlPage> SettingsPages
		{
			get
			{
				return new IPropertyControlPage[] { new UsingDirectiveGroupControl(this) };
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Analyzes the document.
		/// </summary>
		/// <param name="document">The document.</param>
		public override void AnalyzeDocument(CodeDocument document)
		{
			if (document == null) throw new ArgumentNullException("document");

			SettingsManager settingsManager = ServiceLocator.GetService<SettingsManager>();
			if (settingsManager == null) ServiceLocator.RegisterService<SettingsManager>(new SettingsManager());

			this._analyzerRegistry = ServiceLocator.GetService<AnalyzerRegistry>();
			if (this._analyzerRegistry == null)
			{
				this._analyzerRegistry = new AnalyzerRegistry();
				ServiceLocator.RegisterService<AnalyzerRegistry>(this._analyzerRegistry);

				this._analyzerRegistry.AddAnalyzer(new UsingDirectivesAnalyzer());
				this._analyzerRegistry.AddAnalyzer(new NamingAnalyzer());
				this._analyzerRegistry.AddAnalyzer(new SpacingAnalyzer());
			}

			this._analyzerRegistry.RuleAnalyzers.ForEach(analyzer => analyzer.Initialize(this));
			this._analyzerRegistry.RuleAnalyzers.ForEach(analyzer => analyzer.VisitBeforeAnalysis(document));

			CsDocument csharpDocument = (CsDocument)document;

			csharpDocument.WalkDocument(this.VisitElement,
				new CodeWalkerStatementVisitor<object>(this.VisitStatement),
				new CodeWalkerExpressionVisitor<object>(this.VisitExpression),
				new CodeWalkerQueryClauseVisitor<object>(this.VisitQueryClause));

			csharpDocument.Tokens.ForEach(token =>
				this._analyzerRegistry.RuleAnalyzers.ForEach(analyzer =>
					analyzer.VisitToken(document, token)));

			this._analyzerRegistry.RuleAnalyzers.ForEach(analyzer => analyzer.VisitAfterAnalysis(document));
		}

		private bool VisitElement(CsElement element, CsElement parentElement, object context)
		{
			this._analyzerRegistry.RuleAnalyzers.ForEach(analyzer => analyzer.VisitElement(element, parentElement, context));

			return true;
		}

		private bool VisitStatement(Statement statement, Expression parentExpression, Statement parentStatement,
										   CsElement parentElement, object context)
		{
			this._analyzerRegistry.RuleAnalyzers.ForEach(
				analyzer => analyzer.VisitStatement(statement, parentExpression, parentStatement, parentElement, context));

			return true;
		}

		private bool VisitExpression(Expression expression, Expression parentExpression, Statement parentStatement,
											CsElement parentElement, object context)
		{
			this._analyzerRegistry.RuleAnalyzers.ForEach(
				analyzer => analyzer.VisitExpression(expression, parentExpression, parentStatement, parentElement, context));

			return true;
		}

		private bool VisitQueryClause(QueryClause clause, QueryClause parentClause, Expression parentExpression,
											 Statement parentStatement, CsElement parentElement, object context)
		{
			this._analyzerRegistry.RuleAnalyzers.ForEach(
				analyzer => analyzer.VisitQueryClause(clause, parentClause, parentExpression, parentStatement, parentElement, context));

			return true;
		}

		#endregion
	}
}